package com.shiwaixiangcun.authz.shiro.filter;

import com.shiwaixiangcun.authz.shiro.oauth.OAuth2Token;
import com.shiwaixiangcun.core.domain.account.Account;
import com.shiwaixiangcun.core.domain.oauth.AccessToken;
import com.shiwaixiangcun.core.service.oauth.OAuthRSService;
import com.shiwaixiangcun.core.shiro.AccessTokenContextHolder;
import com.shiwaixiangcun.core.shiro.exception.OAuth2TokenAuthenticationException;
import com.shiwaixiangcun.core.web.response.MonkeyResponseDto;
import com.shiwaixiangcun.core.web.response.ResponseCode;
import com.shiwaixiangcun.utils.WebUtils;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class OAuth2AuthenticationFilter extends AuthenticatingFilter implements InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2AuthenticationFilter.class);

    @Autowired
    private OAuthRSService rsService;
    private String resourceId;



    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        AccessToken accessToken = AccessTokenContextHolder.get();
        LOGGER.debug("Set account and clientId from AccessToken: {}", accessToken.getTokenId());
        String account = accessToken.getAccount();
        httpRequest.setAttribute(OAuth.OAUTH_CLIENT_ID, accessToken.getClientId());

        return new OAuth2Token(accessToken.getTokenId(), resourceId).setAccount(account);
    }

    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        AccessToken accessToken = AccessTokenContextHolder.get();


        if (accessToken == null) {

            responseTokenInvalidError(request, response);
            return false;
        }

        if (loginSystemInvalidError(accessToken)) {
            responseSystemInvalidError(request, response);
            return false;
        }

//        Subject subject = getSubject(request, response);
//        if (subject.isAuthenticated()) {
//            return true;
//        }

        return executeLogin(request, response);
    }

    private boolean loginSystemInvalidError(AccessToken token) {
        Account account = rsService.loadAccount(token.getAccount());
        return !account.getScope().contains(resourceId);
    }

    private void responseSystemInvalidError(ServletRequest request, ServletResponse response) throws IOException {
        if (WebUtils.isAjaxRequest((HttpServletRequest) request)) {
            MonkeyResponseDto responseDto = new MonkeyResponseDto();
            responseDto.fail(ResponseCode.NOT_LOGIN, "account not login");
            WebUtils.writeJSON(response, responseDto);
            return;
        }
        redirectToLogin(request, response);
    }

    private void responseTokenInvalidError(ServletRequest request, ServletResponse response) throws IOException {
        if (WebUtils.isAjaxRequest((HttpServletRequest) request)) {
            MonkeyResponseDto responseDto = new MonkeyResponseDto();
            responseDto.fail(ResponseCode.OAUTH2_TOKEN_INVALID_ERROR, "请先登录");
            WebUtils.writeJSON(response, responseDto);
            return;
        }
        redirectToLogin(request, response);
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request,
                                     ServletResponse response) {
        MonkeyResponseDto responseDto = new MonkeyResponseDto();

        if (ae instanceof OAuth2TokenAuthenticationException) {
            responseDto.fail(ResponseCode.OAUTH2_TOKEN_INVALID_ERROR, ae.getMessage());
        } else {
            responseDto.fail(ResponseCode.LOGIN_FAIL, ae.getMessage());
        }
        WebUtils.writeJSON(response, responseDto);
        return false;
    }

    public void setResourceId(String resourceId) {
        this.resourceId = resourceId;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.hasText(resourceId);
    }
}
